package com.spring.framework.webmvc.servlet;

import com.spring.framework.annotation.MyController;
import com.spring.framework.annotation.MyRequestMapping;
import com.spring.framework.context.MyApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ππ
 * @date 2020-11-16 16:32
 * @desc servlet的职责：委派和分发，不做其他任何事情
 */
public class MyDispatcherServlet extends HttpServlet {
    /**声明ApplicationContext，可以简单理解为工厂类*/
    private MyApplicationContext applicationContext;
    /**存储HandlerMapping*/
    private List<MyHandlerMapping> handlerMappings = new ArrayList<>();
    /**存储HandlerMapping与HandlerAdapter映射关系*/
    private Map<MyHandlerMapping,MyHandlerAdapter> handlerAdapterMap = new HashMap<>();
    /**缓存ViewResolver*/
    private List<MyViewResolver> viewResolvers = new ArrayList<>();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            try {
                processDispatchResult(req,resp,new MyModelAndView("500"));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化ApplicationContext,由ApplicationContext进行Ioc和DI
        applicationContext = new MyApplicationContext(config.getInitParameter("configLocations"));
        // 初始化MVC九大组件,仅实现其中的HandlerMapping、HandlerAdapter、ViewResolver
        this.initStrategies(applicationContext);

    }

    /**
     * 请求分发
     * @param req
     * @param resp
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1、获取对应的HandlerMapping
        MyHandlerMapping handlerMapping =getHandler(req);
        if(handlerMapping == null) {
            processDispatchResult(req,resp,new MyModelAndView("404"));
        }
        //2、根据一个HandlerMapping获取一个HandlerAdapter
        MyHandlerAdapter handlerAdapter =getHandlerAdapter(handlerMapping);
        //3、解析方法的形参和返回值之后，封装成ModelAndView
        MyModelAndView modelAndView = handlerAdapter.handler(req,resp,handlerMapping);
        //4、ModelAndView经过ViewResolver处理，最终统一变为View
        //经过view渲染，输出到页面展示
        processDispatchResult(req,resp,modelAndView);
    }
    /**
     * 获取HandlerMapping
     * @param req
     * @return
     */
    private MyHandlerMapping getHandler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()) {
            return null;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        for(MyHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if(matcher.matches()) {
                return handlerMapping;
            }
        }
        return null;
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, MyModelAndView myModelAndView) throws Exception {
        if(this.viewResolvers.isEmpty() || myModelAndView == null) {
            return;
        }
        for(MyViewResolver viewResolver : this.viewResolvers) {
            MyView view = viewResolver.resolveViewName(myModelAndView.getViewName());
            view.render(myModelAndView.getModel(),req,resp);
            return;
        }
    }
    private MyHandlerAdapter getHandlerAdapter(MyHandlerMapping handlerMapping) {
        if(this.handlerAdapterMap.isEmpty()) {
            return null;
        }
        return this.handlerAdapterMap.get(handlerMapping);
    }

    /**
     * 初始化MVC的九大组件，并且顺序不能乱
     * @param context
     */
    private void initStrategies(MyApplicationContext context) {
        //1、初始化多文件上传组件
        initMultipartResolver(context);
        //2、初始化本地语言环境组件
        initLocaleResolver(context);
        //3、初始化主题模板组件
        initThemeResolver(context);
        //4、初始化URL映射组件
        initHandlerMappings(context);
        //5、初始化动态参数适配组件
        initHandlerAdapters(context);
        //6、初始化异常拦截器组件
        initHandlerExceptionResolvers(context);
        //7、初始化视图提取器组件
        initRequestToViewNameTranslator(context);
        //8、初始化视图解析器组件
        initViewResolvers(context);
        //9、初始化参数缓存器组件，用于转发、重定向传值
        initFlushMapManager(context);
    }

    /**
     * 初始化多文件上传组件
     * @param context
     */
    private void initMultipartResolver(MyApplicationContext context) {
    }

    /**
     * 初始化本地语言环境组件
     * @param context
     */
    private void initLocaleResolver(MyApplicationContext context) {
    }

    /**
     * 初始化主体模板组件
     * @param context
     */
    private void initThemeResolver(MyApplicationContext context) {
    }

    /**
     * 初始化URL映射组件
     * @param context
     */
    private void initHandlerMappings(MyApplicationContext context) {
        if(this.applicationContext.getBeanDefinitionCount() == 0) {
            return;
        }
        for(String beanName : this.applicationContext.getBeanDefinitionNames()){
            Object instance = this.applicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();
            if(!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }
            String baseUrl = "";
            if(clazz.isAnnotationPresent(MyRequestMapping.class)) {
                baseUrl = clazz.getAnnotation(MyRequestMapping.class).value().trim();
            }
            // 遍历Controller中的所有public方法
            for (Method method :clazz.getMethods()) {
                // 仅处理具有@MyRequestMapping注解的方法
                if(!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                // 将url处理成正则表达式格式
                String regex = ("/" + baseUrl + "/" +requestMapping.value().trim().replaceAll("\\*",".*")).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                this.handlerMappings.add(new MyHandlerMapping(pattern,method,instance));
                System.out.println("Mapped:" + regex +"," +method);
            }
        }
    }

    /**
     * 初始化动态参数适配组件
     * @param context
     */
    private void initHandlerAdapters(MyApplicationContext context) {
        for(MyHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapterMap.put(handlerMapping,new MyHandlerAdapter());
        }
    }

    /**
     * 初始化异常拦截器组件
     * @param context
     */
    private void initHandlerExceptionResolvers(MyApplicationContext context) {
    }

    /**
     * 初始化视图提取器组件
     * @param context
     */
    private void initRequestToViewNameTranslator(MyApplicationContext context) {
    }

    /**
     * 初始化视图解析器组件
     * @param context
     */
    private void initViewResolvers(MyApplicationContext context) {
        String templateRoot = this.applicationContext.getReader().getConfig().getProperty("templateRoot");
        // 理论上应该考虑递归和map结合匹配，此处简化处理
        this.viewResolvers.add(new MyViewResolver(templateRoot));
    }

    /**
     * 初始化参数缓存组件
     * @param context
     */
    private void initFlushMapManager(MyApplicationContext context) {
    }

}
